import { isFunction, isObject, isEmpty } from 'lodash-es'
import { isPromise, getAndRemoveValue } from '../utils'
import { controlSize } from '@lib/default'

/**
 * 基本的弹框mixin
 *
 * @param {*} config.beforeShow 弹框显示之前的执行方法，支持异步，接受的参数为调用show方法传入的参数，返回Primsie错误或者false的话则弹框不显示
 * @param {*} config.render 弹框内部的内容
 * @param {*} config.getDialogAttrs 获取绑定给el-dialog的属性
 * @param {*} config.getDialogListeners 获取绑定给el-dialog的监听事件
 * @param {boolean|object} config.footer
 * @param {*} config.footer.show 是否显示, 默认为true
 * @param {*} config.footer.cancelButtonText 取消按钮的文字，默认"关闭"
 * @param {*} config.footer.confirmButtonText 确认按钮的文字，默认"确定"
 * @param {*} config.footer.clickConfirmButton 确定按钮的回调，接受一个回调参数，用来关闭弹框
 */
export function basePopupMixins(config = {}) {
  const conf = normalizeConfig(config)

  // 是否正在显示中
  // 即调用了show方法，但是visible还未被赋值为true的这个过程
  let showing = false

  const options = {
    data() {
      return {
        basePopupMixins__visible: false,
        basePopupMixins__dialogAttrs: {},
        basePopupMixins__isContentLoading: false,
        basePopupMixins__isConfirmButtonLoading: false,
        basePopupMixins__contentKey: 0
      }
    },

    provide() {
      return {
        // 统一设置后代表单控件的尺寸大小
        elFormItem: {
          elFormItemSize: controlSize
        }
      }
    },

    created() {
      this.basePopupMixins__setIsAppendToBody()
      this.$on('content-loading', (val) => {
        this.basePopupMixins__isContentLoading = val
      })
      this.$on('confirm-btn-loading', (val) => {
        this.basePopupMixins__isConfirmButtonLoading = val
      })
      this.$on('reload', () => {
        // 正在显示中，如果reload的话，会出现遮罩层闪烁的问题，加个setTimeout就好了
        if (showing) {
          setTimeout(() => {
            this.basePopupMixins__contentKey = +new Date()
          })
        } else {
          this.basePopupMixins__contentKey = +new Date()
        }
      })
      this.$on('close', () => {
        this.basePopupMixins__close()
      })
    },

    methods: {
      async show() {
        showing = true

        try {
          const isShow = await conf.beforeShow.apply(this, Array.from(arguments))
          isShow !== false && (this.basePopupMixins__visible = true)
        } finally {
          showing = false
        }
      },
      basePopupMixins__close() {
        this.basePopupMixins__visible = false
        this.basePopupMixins__isConfirmButtonLoading = false
        this.basePopupMixins__isContentLoading = false
      },
      /**
       * 判断父组件链上是否有el-dialog, 有就添加append-to-body
       */
      basePopupMixins__setIsAppendToBody() {
        let parent = this.$parent
        while (parent) {
          if (parent.$options.name === 'ElDialog') {
            this.$set(this.basePopupMixins__dialogAttrs, 'appendToBody', true)
            return
          } else {
            parent = parent.$parent
          }
        }
      }
    }
  }

  // 定义render函数
  configRender(options, conf)

  return options
}

/**
 * 规范化config
 */
function normalizeConfig(config) {
  const conf = { ...config }
  conf.beforeShow = conf.beforeShow || (() => true)
  conf.getDialogAttrs = conf.getDialogAttrs || (() => ({}))
  conf.getDialogListeners = conf.getDialogListeners || (() => ({}))

  return conf
}

/**
 * 配置render
 */
function configRender(options, conf) {
  options.beforeCreate = function() {
    const render = conf.render || this.$options.render
    this.$options.render = function(h) {
      // el-dialog属性
      const attrs = conf.getDialogAttrs.call(this)
      attrs.visible = this.basePopupMixins__visible
      Object.assign(attrs, this.basePopupMixins__dialogAttrs)
      const _class = getAndRemoveValue(attrs, 'class')
      const _style = getAndRemoveValue(attrs, 'style')
      const ref = getAndRemoveValue(attrs, 'ref')

      // el-dialog监听事件
      const listeners = conf.getDialogListeners.call(this)
      const updateVisible = listeners['update:visible']
      listeners['update:visible'] = (val) => {
        this.basePopupMixins__visible = val
        updateVisible && updateVisible(val)
      }

      return h(
        'ElDialog',
        {
          class: _class,
          style: _style,
          attrs,
          ref,
          on: listeners,
        },
        [
          h(
            'div',
            {
              key: this.basePopupMixins__contentKey,
              directives: [
                { name: 'loading', value: this.basePopupMixins__isContentLoading }
              ]
            },
            [render.apply(this, Array.from(arguments))]
          ),
          withFooter.call(this, h, conf)
        ]
      )
    }
  }
}

/**
 * 添加底部按钮
 */
function withFooter(h, conf) {
  const footerConf = isFunction(conf.footer) && conf.footer.call(this)
  if (footerConf === false) {
    return ''
  }

  const footer = {
    useCancelButton: false,
    useConfirmButton: true,
    cancelButtonText: '关闭',
    confirmButtonText: '确定',
    clickConfirmButton: null
  }
  if (isObject(footerConf)) {
    Object.assign(footer, footerConf)
  }

  const vnodes = []
  if (footer.useCancelButton) {
    vnodes.push(h(
      'ElButton',
      {
        props: { plain: true, type: 'info' },
        on: {
          click: this.basePopupMixins__close
        }
      },
      [footer.cancelButtonText]
    ))
  }
  if (footer.useConfirmButton) {
    vnodes.push(h(
      'ElButton',
      {
        props: { type: 'primary', loading: this.basePopupMixins__isConfirmButtonLoading },
        on: {
          click: () => {
            const showBtnLoading = () => (this.basePopupMixins__isConfirmButtonLoading = true)
            const cancelBtnLoading = () => (this.basePopupMixins__isConfirmButtonLoading = false)
            if (footer.clickConfirmButton) {
              const res = footer.clickConfirmButton({
                close: this.basePopupMixins__close,
                showLoading: showBtnLoading,
                cancelLoading: cancelBtnLoading
              })
              if (isPromise(res)) {
                showBtnLoading()
                res.finally(() => {
                  setTimeout(cancelBtnLoading, 500)
                })
              }
            } else {
              this.basePopupMixins__close()
            }
          }
        }
      },
      [footer.confirmButtonText]
    ))
  }


  return !isEmpty(vnodes)
    ? h(
      'div',
      {
        style: 'display: flex;justify-content: flex-end;margin-top:20px'
      },
      vnodes
    )
    : ''
}
